package com.winter.boot.context;


import com.winter.boot.annotation.Controller;
import com.winter.boot.annotation.RequestMapping;
import com.winter.boot.annotation.Service;
import com.winter.boot.annotation.WinterBootApplication;
import com.winter.boot.config.BeanFactory;
import com.winter.boot.config.DefaultBeanFactory;
import com.winter.boot.config.RootBeanDefinition;
import com.winter.boot.util.ClassUtils;
import com.winter.boot.web.handler.HttpServerHandler;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

public class DefaultApplicationContext implements ApplicationContext {

    /**
     * Synchronization monitor for the "refresh" and "destroy"
     */
    private final Object startupShutdownMonitor = new Object();
    private final Set<Class<?>> primarySources;
    private DefaultBeanFactory defaultBeanFactory;

    public DefaultApplicationContext(Set<Class<?>> primarySources) {
        this.primarySources = primarySources;
    }

    @Override
    public void refresh() throws InstantiationException, IllegalAccessException {
        synchronized (startupShutdownMonitor) {

            DefaultBeanFactory beanFactory = new DefaultBeanFactory();
            this.defaultBeanFactory = beanFactory;

            scanner(beanFactory);

            registerBeanPostProcessors(beanFactory);
        }
    }

    public void scanner(DefaultBeanFactory beanFactory) throws InstantiationException, IllegalAccessException {
        for (Class<?> primarySource : primarySources) {
            WinterBootApplication annotation = primarySource.getAnnotation(WinterBootApplication.class);
            if (annotation != null) {
                String[] packages = annotation.scanBasePackages();
                if (packages != null) {
                    for (String aPackage : packages) {
                        Set<Class<?>> classes = ClassUtils.getClasses(aPackage, true);
                        for (Class<?> aClass : classes) {
                            String beanName = aClass.getName();
                            Service service = aClass.getAnnotation(Service.class);
                            if (service != null) {
                                String value = service.value();
                                if (value != null) {
                                    RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
                                    rootBeanDefinition.setBeanClass(aClass);
                                    createBean(beanName, rootBeanDefinition);
                                }
                            }
                            Controller controller = aClass.getAnnotation(Controller.class);
                            if (controller != null) {
                                Method[] methods = aClass.getDeclaredMethods();
                                for (Method method : methods) {
                                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                                    if (requestMapping != null) {
                                        HttpServerHandler.addHandler(controller.value() + requestMapping.value(), beanName + "." + method.getName());
                                    }
                                }
                            }

                            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
                            rootBeanDefinition.setBeanClass(aClass);
                            createBean(beanName, rootBeanDefinition);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void registerBeanPostProcessors(BeanFactory beanFactory) throws IllegalAccessException {
        getDefaultBeanFactory().registerBeanPostProcessors();
    }


    public DefaultBeanFactory getDefaultBeanFactory() {
        return defaultBeanFactory;
    }

    @Override
    public void createBean(String beanName, RootBeanDefinition beanDefinition) {
        getDefaultBeanFactory().createBean(beanName, beanDefinition);
    }

    @Override
    public Object getBean(String beanName) {
        return getDefaultBeanFactory().getBean(beanName);
    }

    @Override
    public List<Class<?>> getParams(String beanName) {
        return null;
    }
}
